/*****************************************************************************
*                     Organizacion de Datos - 75.06                          *
*                         Catedra Lic. Servetto                              *
*            Facultad de Ingenieria - Universidad de Buenos Aires            *
******************************************************************************/

#include "ParseadorComandos.h"
#include "AdministradorDeOperaciones.h"
#include "AdministradorMetadata.h"

#include <list>
#include <string>
#include <fstream>
#include <map>

using namespace Comun;

namespace CapaConsulta 
{

	void ParseadorComandos::SacarConComprobacion(list<string>* operacion, string nombreOperacion) {
		
		operacion->pop_front();
		if (operacion->empty())
		{
			cerr << "No se puede seguir parseando la operacion " << nombreOperacion << ". La operacion no termina correctamente." << endl;
			throw new exception();
		}
	}
	
	bool ParseadorComandos::ValidarDatosEntreComillasSimples(string dato) {
		
		if (dato[0] != '\'' || dato[dato.length() - 1] != '\'')
			return false;
		
		return true;
	};

	string ParseadorComandos::EliminarComillasSimplesDeDatos(string dato) {
		
		string resultado;
		for(int i = 1; i < dato.length() - 1; ++i)
		{
			resultado += dato[i];
		}
		return resultado;
	};
	
	bool ParseadorComandos::ValidarNombreAtributoConClase(string nombreAtributoConClase) {
		
		for(int i = 0; i < nombreAtributoConClase.length(); ++i)
		{
			if (nombreAtributoConClase[i] == '.')
				return true;
		}
		return false;
	}
	
	string ParseadorComandos::BuscarNombreClase(string nombreAtributoConClase) {
		
		string resultado;
		bool encontrePunto = false;
		for(int i = 0; i < nombreAtributoConClase.length(); ++i)
		{
			if (nombreAtributoConClase[i] != '.')
				resultado += nombreAtributoConClase[i];
			else
			{
				encontrePunto = true;
				break;
			}
		}
		return resultado;
	};

	string ParseadorComandos::BuscarNombreAtributo(string nombreAtributoConClase) {
		
		string resultado;
		bool encontrePunto = false;
		for(int i = 0; i < nombreAtributoConClase.length(); ++i)
		{
			if(encontrePunto)
			{
				resultado += nombreAtributoConClase[i];
				continue;
			}
			
			if (nombreAtributoConClase[i] == '.')
				encontrePunto = true;
			
		}
		return resultado;
	};
	
	CondicionCompuesta* ParseadorComandos::ParsearWhere(string nombreOperacion, list<string>* operacion, map<string, AdministradorMetadata*> administradores, bool atributosConPunto, bool esperaProxima, string proximaSentencia) {
		
		CondicionCompuesta* condicionCompuesta;
		
		string whereSupuesto = operacion->front();
		if (whereSupuesto != "WHERE")
		{
			cerr << "La operacion " << nombreOperacion << "espera un comando WHERE, en ves de eso se encontro " << whereSupuesto << "." << endl;
			throw new exception();
		}
		ParseadorComandos::SacarConComprobacion(operacion, nombreOperacion);
		
		Condicion* subcondicion = ParseadorComandos::ParsearCondicion(operacion, administradores, atributosConPunto);
		
		if (operacion->empty())
		{
			condicionCompuesta = new CondicionCompuestaAND();
			condicionCompuesta->Subcondiciones.push_back(subcondicion);
			return condicionCompuesta;
		}
		
		string orAndOProximaSupuesto = operacion->front();
		if (esperaProxima)
		{
			if(orAndOProximaSupuesto != "AND" && orAndOProximaSupuesto != "OR" && orAndOProximaSupuesto != proximaSentencia)
			{
				cerr << "La operacion " << nombreOperacion << "espera un comando OR, AND o " << proximaSentencia << " unicamente en esta posicion, en ves de eso se encontro " << orAndOProximaSupuesto << "." << endl;
				throw new exception();
			}

			if (orAndOProximaSupuesto != proximaSentencia)
				ParseadorComandos::SacarConComprobacion(operacion, nombreOperacion);
		}
		else
		{
			if(orAndOProximaSupuesto != "AND" && orAndOProximaSupuesto != "OR")
			{
				cerr << "La operacion " << nombreOperacion << "espera un comando OR o AND unicamente en esta posicion, en ves de eso se encontro " << orAndOProximaSupuesto << "." << endl;
				throw new exception();
			}
			ParseadorComandos::SacarConComprobacion(operacion, nombreOperacion);
		}
		
		if (orAndOProximaSupuesto == proximaSentencia)
		{
			condicionCompuesta = new CondicionCompuestaAND();
			condicionCompuesta->Subcondiciones.push_back(subcondicion);
			return condicionCompuesta;
		}
		
		string operacionCompuestaCadena = "AND";
		if (orAndOProximaSupuesto == "AND")
		{
			condicionCompuesta = new CondicionCompuestaAND();			
		}
		else
		{
			condicionCompuesta = new CondicionCompuestaOR();
			operacionCompuestaCadena = "OR";
		}

		condicionCompuesta->Subcondiciones.push_back(subcondicion);
		
		while (true)
		{
			Condicion* subcondicionDeLoop = ParseadorComandos::ParsearCondicion(operacion, administradores, atributosConPunto);
			condicionCompuesta->Subcondiciones.push_back(subcondicionDeLoop);

			if (operacion->empty())
			{
				break;
			}
			
			string operacionCompuestaCadenaSupuesto = operacion->front();
			
			if (esperaProxima)
			{
				if(operacionCompuestaCadenaSupuesto == proximaSentencia)
				{
					condicionCompuesta->Subcondiciones.push_back(subcondicionDeLoop);
					break;
				}
				ParseadorComandos::SacarConComprobacion(operacion, nombreOperacion);
			}
			
			if(operacionCompuestaCadenaSupuesto != operacionCompuestaCadena)
			{
				cerr << "La operacion " << nombreOperacion << " espera todos conectores del mismo tipo en la condicion WHERE, se esperaba un conector del tipo " << operacionCompuestaCadena << " pero se encontro un " << operacionCompuestaCadenaSupuesto << "." << endl;
				throw new exception();
			}
			ParseadorComandos::SacarConComprobacion(operacion, nombreOperacion);
			
			
		}
		
		return condicionCompuesta;
	};
	
	Condicion* ParseadorComandos::ParsearCondicion(list<string>* operacion, map<string, AdministradorMetadata*> administradores, bool atributosConPunto) {
		
		Condicion* condicion;
		list<string>::iterator cadenasCondicion = operacion->begin();
		
		if (cadenasCondicion == operacion->end())
		{
			cerr << "No se puede parsear la condicion se esperaban condiciones de la forma {Atributo} operador valor." << endl;
			throw new exception();
		}
		string atributoSupuesto = (*cadenasCondicion);
		
		cadenasCondicion++;
		if (cadenasCondicion == operacion->end())
		{
			cerr << "No se puede parsear la condicion se esperaban condiciones de la forma Atributo {operador} valor." << endl;
			throw new exception();
		}
		string operadorSupuesto = (*cadenasCondicion);
		
		if (operadorSupuesto != "=" && operadorSupuesto != ">" && operadorSupuesto != "<" && operadorSupuesto != ">=" && operadorSupuesto != "<=")
		{
			cerr << "No se puede parsear el operador de la condicion, se esperaba un =, >, <, >= o <= y se encontro un " << operadorSupuesto << "." << endl;
		}
		
		cadenasCondicion++;
		if (cadenasCondicion == operacion->end())
		{
			cerr << "No se puede parsear la condicion se esperaban condiciones de la forma Atributo operador {valor}." << endl;
			throw new exception();
		}
		
		string valorSupuesto = (*cadenasCondicion);
		
		string nombreClase;
		string nombreAtributo;
		
		if (atributosConPunto)
		{
			if (!ParseadorComandos::ValidarNombreAtributoConClase(atributoSupuesto))
			{
				cerr << "No se puede parsear el atributo " << atributoSupuesto << ". El formato del atributo debe ser 'Clase.Atributo'." << endl;
				throw new exception();
			}
			
			nombreClase = ParseadorComandos::BuscarNombreClase(atributoSupuesto);
			nombreAtributo = ParseadorComandos::BuscarNombreAtributo(atributoSupuesto);
		}
		else
		{
			nombreClase = (*administradores.begin()).second->ClaseMetadata->NombreClase->c_str();
			nombreAtributo = atributoSupuesto;
		}
		
		if (administradores.find(nombreClase) == administradores.end())
		{
			cerr << "Se expreso una condicion sobre la clase " << nombreClase << ", pero esta clase no esta definida en la operacion." << endl;
			throw new exception();
		}
		
		AdministradorMetadata* administrador = (*administradores.find(nombreClase)).second;
		
		if (!administrador->EsAtributoDeLaClase(nombreAtributo))
		{
			cerr << "El atributo " << nombreAtributo << " de la condicion no pertenece a la clase " << nombreClase << "." << endl;
			throw new exception();
		}
		
		if (!administrador->ElValorDelAtributoEsCorrecto(nombreAtributo, valorSupuesto))
		{
			cerr << "No se puede parsear correctamente el tipo del atributo " << nombreAtributo << " en la condicion. El valor '" << valorSupuesto << "' no es correcto." << endl;
			throw new exception();
		}
		
		string valorLimpio;
		
		if (valorSupuesto[0] == '\'')
		{
			if(!ParseadorComandos::ValidarDatosEntreComillasSimples(valorSupuesto))
			{
				cerr << "No se puede parsear correctamente el tipo del atributo " << nombreAtributo << " en la condicion. El valor " << valorSupuesto << " no es correcto." << endl;
				throw new exception();
			}
			valorLimpio = ParseadorComandos::EliminarComillasSimplesDeDatos(valorSupuesto);
		}
		else
			valorLimpio = valorSupuesto;
		
		
		Atributo* objetoAtributo = administrador->BuscarAtributoPorNombre(nombreAtributo);
		objetoAtributo->Valor = valorLimpio;
		
		if (operadorSupuesto == "=")
			condicion = new CondicionIgual();
		else if (operadorSupuesto == ">")
			condicion = new CondicionMayor();
		else if (operadorSupuesto == "<")
			condicion = new CondicionMenor();
		else if (operadorSupuesto == ">=")
			condicion = new CondicionMayorIgual();
		else if (operadorSupuesto == "<=")
			condicion = new CondicionMenorIgual();
		
		condicion->ValorCondicion = *objetoAtributo;
		condicion->NombreClase = administrador->ClaseMetadata->NombreClase->c_str();

		operacion->pop_front();
		operacion->pop_front();
		operacion->pop_front();
				
		return condicion;
	};
	
}
